<!DOCTYPE html>
<html>
<head>
<title>Spherical Pendulum</title>
</head>
<body>

<canvas id="pendulumCanvas" width="500" height="450"></canvas>

<script>
const canvas = document.getElementById('pendulumCanvas');
const ctx = canvas.getContext('2d');

// Style settings
ctx.lineWidth = 2;
ctx.strokeStyle = 'black';
ctx.fillStyle = 'black';
ctx.font = 'italic 22px "Times New Roman"';
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';

// --- Drawing functions ---
function drawArrow(fromx, fromy, tox, toy, headlen = 10) {
    const dx = tox - fromx;
    const dy = toy - fromy;
    const angle = Math.atan2(dy, dx);
    ctx.beginPath();
    ctx.moveTo(fromx, fromy);
    ctx.lineTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle - Math.PI / 6), toy - headlen * Math.sin(angle - Math.PI / 6));
    ctx.moveTo(tox, toy);
    ctx.lineTo(tox - headlen * Math.cos(angle + Math.PI / 6), toy - headlen * Math.sin(angle + Math.PI / 6));
    ctx.stroke();
}

// --- Parameters ---
const cx = 250;
const cy = 120; // Origin point (suspension)
const l_canvas = 160; // Length of the pendulum string in pixels
const theta = 38 * Math.PI / 180; // Angle theta in radians, adjusted for aesthetics

const mass_pos = {
    x: cx + l_canvas * Math.sin(theta),
    y: cy + l_canvas * Math.cos(theta)
};
const ellipse_center = { x: cx, y: mass_pos.y };
const ellipse_rx = l_canvas * Math.sin(theta);
const ellipse_ry = ellipse_rx * 0.35;

// --- Start Drawing ---
ctx.clearRect(0, 0, canvas.width, canvas.height);

// 1. Coordinate Axes
// z-axis (solid)
drawArrow(cx, ellipse_center.y + 70, cx, cy - 80);
ctx.fillText('z', cx - 20, cy - 80);
// x-axis (solid)
drawArrow(cx - 20, cy, cx + 80, cy);
ctx.fillText('x', cx + 80 + 15, cy);

// 2. Dashed ellipse for path of motion
ctx.beginPath();
ctx.setLineDash([6, 4]);
ctx.ellipse(ellipse_center.x, ellipse_center.y, ellipse_rx, ellipse_ry, 0, 0, 2 * Math.PI);
ctx.stroke();
ctx.setLineDash([]); // Reset to solid lines

// 3. Pendulum string
ctx.beginPath();
ctx.moveTo(cx, cy);
ctx.lineTo(mass_pos.x, mass_pos.y);
ctx.stroke();

// 4. Labels for length 'l' and mass 'm'
// 'l' label
ctx.save();
ctx.translate(cx + (mass_pos.x - cx) / 2, cy + (mass_pos.y - cy) / 2);
ctx.rotate(theta);
ctx.fillText('l', 20, -12);
ctx.restore();
// Arrow indicating the mass point
const massArrowEndX = mass_pos.x + 40;
drawArrow(mass_pos.x, mass_pos.y, massArrowEndX, mass_pos.y);
// 'm' label
ctx.fillText('m', massArrowEndX + 15, mass_pos.y);

// 5. Angle theta
// Dashed line for angle reference from origin along z-axis
ctx.beginPath();
ctx.setLineDash([4, 4]);
ctx.moveTo(cx, cy);
ctx.lineTo(cx, mass_pos.y);
ctx.stroke();
ctx.setLineDash([]);
// Arc for theta angle
ctx.beginPath();
ctx.arc(cx, cy, 35, Math.PI / 2, Math.PI / 2 + theta);
ctx.stroke();
// Theta label
ctx.fillText('θ', cx + 25, cy + 25);

// 6. Projection lines & central axis details
// Dashed horizontal line from mass to z-axis
ctx.beginPath();
ctx.setLineDash([4, 4]);
ctx.moveTo(mass_pos.x, mass_pos.y);
ctx.lineTo(cx + 10, mass_pos.y); // Shorten slightly to not overlap the cross
ctx.stroke();
ctx.setLineDash([]);
// "Plus" sign at circle center on the z-axis
ctx.beginPath();
ctx.moveTo(cx - 10, ellipse_center.y);
ctx.lineTo(cx + 10, ellipse_center.y);
ctx.moveTo(cx, ellipse_center.y - 10);
ctx.lineTo(cx, ellipse_center.y + 10);
ctx.stroke();
// Dashed horizontal ticks on z-axis within the circle path
ctx.beginPath();
ctx.setLineDash([4, 4]);
ctx.moveTo(cx-10, ellipse_center.y - 25);
ctx.lineTo(cx+10, ellipse_center.y - 25);
ctx.moveTo(cx-10, ellipse_center.y + 25);
ctx.lineTo(cx+10, ellipse_center.y + 25);
ctx.stroke();
ctx.setLineDash([]);

// 7. Rotation indicator (phi-dot)
// Curved arrow on the path
const curveArrowBaseY = ellipse_center.y + ellipse_ry;
const curveArrowRadiusX = ellipse_rx * 0.7;
const startX = cx - curveArrowRadiusX;
const endX = cx + curveArrowRadiusX;
ctx.beginPath();
ctx.moveTo(startX, curveArrowBaseY);
ctx.bezierCurveTo(cx - curveArrowRadiusX, curveArrowBaseY + 15, cx + curveArrowRadiusX, curveArrowBaseY + 15, endX, curveArrowBaseY);
ctx.stroke();
// Arrowhead for the curved arrow
const tangentAngle = -0.15; // Small angle for arrowhead orientation
drawArrow(endX - 10 * Math.cos(tangentAngle), endX - 10 * Math.sin(tangentAngle), endX, curveArrowBaseY, 8);
// phi-dot label
ctx.fillText('φ̇', cx, curveArrowBaseY + 15);

// 8. Caption
ctx.font = 'bold 20px "Times New Roman"';
ctx.fillText('Fig. 2.14.', cx, canvas.height - 30);
</script>

</body>
</html>